package com.opensource.component.query.wrapper;

import static com.baomidou.mybatisplus.core.toolkit.StringPool.COMMA;
import static com.baomidou.mybatisplus.core.toolkit.StringPool.NEWLINE;
import static java.util.stream.Collectors.joining;

import com.baomidou.mybatisplus.core.conditions.ISqlSegment;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.opensource.component.query.exception.MpExtendException;
import com.opensource.component.query.matedata.TableMetadataHelper;
import com.opensource.component.query.script.ExtendConstant;
import com.opensource.component.query.script.ExtendMybatisCommand;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

/**
 * @author ZonLen since on 2021/12/18 下午8:09
 */
public final class LambdaExtendWrapper<M> extends
    AbstractExtendWrapper<M, LambdaExtendWrapper<M>> {

  /**
   * 关联表的查询子段
   */
  private final List<SharedString> joinSqlSelect = new ArrayList<>();

  /**
   * 关联表SQL
   */
  private final List<SharedString> joinSql = new ArrayList<>();

  private final List<Class<?>> joinTableClasses = new ArrayList<>();

  /**
   * 不建议直接 new 该实例，使用 Wrappers.lambdaQuery(entity)
   */
  public LambdaExtendWrapper(Class<M> tableClass) {
    super(tableClass);
    super.setEntityClass(tableClass);
  }


  /**
   * 内部条件嵌套使用
   */
  LambdaExtendWrapper(M entity, Class<M> tableClass,
      AtomicInteger paramNameSeq,
      Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments,
      SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
    super(tableClass);
    super.setEntity(entity);
    super.setEntityClass(tableClass);
    this.paramNameSeq = paramNameSeq;
    this.paramNameValuePairs = paramNameValuePairs;
    this.expression = mergeSegments;
    this.lastSql = lastSql;
    this.sqlComment = sqlComment;
    this.sqlFirst = sqlFirst;
  }

  @Override
  public String getSqlSegment() {
    final String sqlSegment = super.getSqlSegment();
    return sqlSegment.replaceAll(ExtendConstant.TABLE_ALIAS_COLUMN_PLACE_HOLDER, getTableAlias());
  }

  public String getTableAlias() {
    if (StringUtils.isNotBlank(tableAlias)) {
      return tableAlias + Constants.DOT;
    }
    return Constants.EMPTY;
  }

  /**
   * 设置查询字段
   */
  @Override
  public String getSqlSelect() {
    if (ExtendMybatisCommand.isSelectCount(currentStatementMethodName)) {
      return null;
    }
    if (selectColumnSegments.isEmpty() && aggregationSelectSegments.isEmpty()) {
      if (StringUtils.isNotBlank(tableAlias)) {
        return TableMetadataHelper.tableMetadata(getEntityClass()).getSelectColumnsAndAlias(null);
      } else {
        return TableInfoHelper.getTableInfo(getEntityClass()).getAllSqlSelect();
      }
    }
    final StringBuilder selectSql = new StringBuilder();
    for (int i = 0; i < selectColumnSegments.size(); i++) {
      if (StringUtils.isNotBlank(tableAlias)) {
        selectSql.append(tableAlias).append(Constants.DOT)
            .append(selectColumnSegments.get(i).getStringValue());
        if (i != selectColumnSegments.size() - 1) {
          selectSql.append(COMMA);
        }
      }
    }
    if (StringUtils.isNotBlank(selectSql.toString()) && CollectionUtils
        .isNotEmpty(aggregationSelectSegments)) {
      selectSql.append(COMMA);
    }
    for (int i = 0; i < aggregationSelectSegments.size(); i++) {
      if (StringUtils.isNotBlank(tableAlias)) {
        selectSql.append(aggregationSelectSegments.get(i).selectAggregationColumn());
        if (i != aggregationSelectSegments.size() - 1) {
          selectSql.append(COMMA);
        }
      }
    }
    return selectSql.toString();
  }

  /**
   * 不能删除 获取join SQL语句 OGNL需要通过get方法获取值
   *
   * @return 构建好的SQL
   */
  public String getJoinSql() {
    StringBuilder sql = new StringBuilder();
    if (CollectionUtils.isNotEmpty(joinSql)) {
      for (SharedString sharedString : joinSql) {
        sql.append(sharedString.getStringValue()).append(NEWLINE);
      }
    }
    return sql.toString();
  }

  /**
   * <p>在单表查询过程，用于过滤JOIN表的查询字段</p>
   * 不能删除 获取join表的查询信息, OGNL需要通过get方法获取值
   */
  public String getJoinSelect() {
    StringBuilder joinSelect = new StringBuilder(StringPool.EMPTY);
    if (CollectionUtils.isNotEmpty(joinSqlSelect)) {
      if (StringUtils.isNotBlank(expression.getNormal().getSqlSegment())) {
        joinSelect.append(COMMA);
      }
      joinSelect.append(joinSqlSelect.stream().map(SharedString::getStringValue)
          .filter(StringUtils::isNotBlank)
          .collect(joining(StringPool.COMMA)));
    }
    return joinSelect.toString();
  }

  /**
   * 用于生成嵌套 sql
   * <p>故 sqlSelect 不向下传递</p>
   */
  @Override
  protected LambdaExtendWrapper<M> instance() {
    final LambdaExtendWrapper<M> innerWrapper = new LambdaExtendWrapper<>(getEntity(),
        getEntityClass(), paramNameSeq,
        paramNameValuePairs, new MergeSegments(), SharedString.emptyString(),
        SharedString.emptyString(), SharedString.emptyString());
    innerWrappers.add(innerWrapper);
    return innerWrapper;
  }

  @Override
  public void clear() {
    super.clear();
    joinSql.clear();
    joinSqlSelect.clear();
  }

  /**
   * LEFT JOIN
   */
  public <J> LambdaExtendWrapper<M> leftJoin(Class<J> joinTableClass,
      Consumer<LambdaJoinWrapper<M, J>> consumer) {
    joinTableClasses.add(joinTableClass);
    final LambdaJoinWrapper<M, J> joinWrapper = new LambdaJoinWrapper<>(joinTableClass, this);
    consumer.accept(joinWrapper);
    joinWrapper.doLeftJoin();
    return this;
  }

  /**
   * RIGHT JOIN
   */
  public <J> LambdaExtendWrapper<M> rightJoin(Class<J> joinTableClass,
      Consumer<LambdaJoinWrapper<M, J>> consumer) {
    joinTableClasses.add(joinTableClass);
    final LambdaJoinWrapper<M, J> joinWrapper = new LambdaJoinWrapper<>(joinTableClass, this);
    consumer.accept(joinWrapper);
    joinWrapper.doRightJoin();
    return this;
  }

  /**
   * INNER JOIN
   */
  public <J> LambdaExtendWrapper<M> innerJoin(Class<J> joinTableClass,
      Consumer<LambdaJoinWrapper<M, J>> consumer) {
    joinTableClasses.add(joinTableClass);
    final LambdaJoinWrapper<M, J> joinWrapper = new LambdaJoinWrapper<>(joinTableClass, this);
    consumer.accept(joinWrapper);
    joinWrapper.doInnerJoin();
    return this;
  }

  /**
   * 添加JOIN SQL片段
   */
  protected void addJoinSql(SharedString sharedString) {
    joinSql.add(sharedString);
  }

  /**
   * 提供给Join 查询参数片段的使用
   */
  protected AtomicInteger getParamNameSeq() {
    return paramNameSeq;
  }

  protected void addJoinSelect(String joinSelectColumns) {
    joinSqlSelect.add(new SharedString(joinSelectColumns));
  }

  /**
   * 添加join 表的条件到where上，如果单表查询使用该API报错，不存在该字段
   */
  public <J> LambdaExtendWrapper<M> eq(Class<J> joinTableClass,
      SFunction<J, Object> joinFiledFunction, Object val) {
    if (!joinTableClasses.contains(joinTableClass)) {
      throw new MpExtendException(
          "Class [" + joinTableClass.getName() + "] is not join table class, please check");
    }
    appendSqlSegments(joinTableFieldAndAlias(joinTableClass, joinFiledFunction),
        SqlKeyword.EQ, () -> formatParam(null, val));
    return this;
  }

  private <J> ISqlSegment joinTableFieldAndAlias(Class<J> joinTableClass,
      SFunction<J, Object> joinFiledFunction) {
    return () -> TableMetadataHelper.tableMetadata(joinTableClass)
        .getColumnAndAlias(joinFiledFunction);
  }


}
